package com.douyinkan.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.douyinkan.dto.ContentDTO;
import com.douyinkan.entity.Content;
import com.douyinkan.mapper.ContentMapper;
import com.douyinkan.service.ContentService;
import com.douyinkan.service.DataSourceManagerService;
import com.douyinkan.service.DirectoryScanService;
import com.douyinkan.util.FileUtil;
import com.douyinkan.util.MediaFileInfo;
import com.douyinkan.util.MediaUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.sql.SQLException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;

@Service
@Transactional
public class ContentServiceImpl extends ServiceImpl<ContentMapper, Content> implements ContentService {

    private static final Logger logger = LoggerFactory.getLogger(ContentServiceImpl.class);

    @Autowired
    private ContentMapper contentMapper;
    
    @Override
    public Page<ContentDTO> getAllContents(Page<ContentDTO> pageable) {
        logger.info("=== Getting all contents ===");
        Page<Content> contentPage = new Page<>(pageable.getCurrent(), pageable.getSize());
        Page<Content> result = contentMapper.selectPage(contentPage, null);
        logger.info("Retrieved {} contents", result.getTotal());
        logger.info("=== Getting all contents completed ===");
        
        // 转换为 DTO 并返回
        Page<ContentDTO> dtoPage = new Page<>();
        dtoPage.setCurrent(result.getCurrent());
        dtoPage.setSize(result.getSize());
        dtoPage.setTotal(result.getTotal());
        dtoPage.setRecords(result.getRecords().stream()
                .map(this::convertToDTO)
                .toList());
        return dtoPage;
    }

    @Override
    public Page<ContentDTO> getContentsByTagId(Long tagId, Page<ContentDTO> pageable) {
        logger.info("=== Getting contents by tag ID: {} ===", tagId);
        // 简化实现，实际中可能需要更复杂的查询
        Page<Content> contentPage = new Page<>(pageable.getCurrent(), pageable.getSize());
        QueryWrapper<Content> wrapper = new QueryWrapper<>();
        // 如果需要按标签查询，这里需要连接标签表
        Page<Content> result = contentMapper.selectPage(contentPage, wrapper);
        logger.info("Retrieved {} contents for tag ID: {}", result.getTotal(), tagId);
        logger.info("=== Getting contents by tag completed ===");
        
        // 转换为 DTO 并返回
        Page<ContentDTO> dtoPage = new Page<>();
        dtoPage.setCurrent(result.getCurrent());
        dtoPage.setSize(result.getSize());
        dtoPage.setTotal(result.getTotal());
        dtoPage.setRecords(result.getRecords().stream()
                .map(this::convertToDTO)
                .toList());
        return dtoPage;
    }

    @Override
    public ContentDTO getContentById(Long id) {
        logger.info("=== Getting content by ID: {} ===", id);
        Content content = contentMapper.selectById(id);
        ContentDTO result = content != null ? convertToDTO(content) : null;
        logger.info("Content found: {}", result != null ? "YES" : "NO");
        logger.info("=== Getting content by ID completed ===");
        return result;
    }
    
    @Override
    public ContentDTO saveContent(ContentDTO contentDTO) {
        logger.info("=== Saving content ===");
        Content content;
        if (contentDTO.getId() != null && contentDTO.getId() > 0) {
            // 更新现有内容
            content = contentMapper.selectById(contentDTO.getId());
            if (content == null) {
                logger.warn("Content not found with id: {}", contentDTO.getId());
                logger.info("=== Saving content failed ===");
                return null;
            }
            
            // 更新内容属性
            content.setFilename(contentDTO.getFilename());
            content.setFilePath(contentDTO.getFilePath());
            content.setContentType(contentDTO.getContentType());
            content.setTitle(contentDTO.getTitle());
            content.setDescription(contentDTO.getDescription());
            content.setDurationSeconds(contentDTO.getDurationSeconds());
            content.setWidth(contentDTO.getWidth());
            content.setHeight(contentDTO.getHeight());
            content.setFileSizeBytes(contentDTO.getFileSizeBytes());
            content.setThumbnailPath(contentDTO.getThumbnailPath());
            content.setUpdatedAt(LocalDateTime.now());
            
            contentMapper.updateById(content);
        } else {
            // 创建新内容
            content = new Content();
            content.setFilename(contentDTO.getFilename());
            content.setFilePath(contentDTO.getFilePath());
            content.setContentType(contentDTO.getContentType());
            content.setTitle(contentDTO.getTitle());
            content.setDescription(contentDTO.getDescription());
            content.setDurationSeconds(contentDTO.getDurationSeconds());
            content.setWidth(contentDTO.getWidth());
            content.setHeight(contentDTO.getHeight());
            content.setFileSizeBytes(contentDTO.getFileSizeBytes());
            content.setThumbnailPath(contentDTO.getThumbnailPath());
            content.setCreatedAt(LocalDateTime.now());
            content.setUpdatedAt(LocalDateTime.now());
            
            contentMapper.insert(content);
        }
        
        logger.info("Content saved successfully: ID {}", content.getId());
        logger.info("=== Saving content completed ===");
        return convertToDTO(content);
    }
    
    @Override
    public boolean rateContent(Long contentId, Integer rating) {
        logger.warn("Rating functionality not implemented yet with MyBatis-Plus");
        // 评分功能暂时不实现，因为需要额外的实体和表
        return false;
    }
    
    @Override
    public boolean toggleFavorite(Long contentId) {
        logger.warn("Favorite functionality not implemented yet with MyBatis-Plus");
        // 收藏功能暂时不实现，因为需要额外的实体和表
        return false;
    }
    
    @Override
    public boolean removeFavorite(Long contentId) {
        logger.warn("Remove favorite functionality not implemented yet with MyBatis-Plus");
        // 移除收藏功能暂时不实现，因为需要额外的实体和表
        return false;
    }
    
    @Override
    public boolean addTagToContent(Long contentId, Long tagId) {
        logger.warn("Tag functionality not implemented yet with MyBatis-Plus");
        // 标签功能暂时不实现，因为需要额外的实体和表
        return false;
    }
    
    @Override
    public boolean removeTagFromContent(Long contentId, Long tagId) {
        logger.warn("Remove tag functionality not implemented yet with MyBatis-Plus");
        // 移除标签功能暂时不实现，因为需要额外的实体和表
        return false;
    }
    
    private ContentDTO convertToDTO(Content content) {
        ContentDTO dto = new ContentDTO();
        dto.setId(content.getId());
        dto.setFilename(content.getFilename());
        dto.setFilePath(content.getFilePath());
        dto.setContentType(content.getContentType());
        dto.setTitle(content.getTitle());
        dto.setDescription(content.getDescription());
        dto.setDurationSeconds(content.getDurationSeconds());
        dto.setWidth(content.getWidth());
        dto.setHeight(content.getHeight());
        dto.setFileSizeBytes(content.getFileSizeBytes());
        dto.setCreatedAt(content.getCreatedAt());
        dto.setUpdatedAt(content.getUpdatedAt());
        dto.setThumbnailPath(content.getThumbnailPath());
        
        return dto;
    }

    @Autowired
    @Lazy
    private DataSourceManagerService dataSourceManagerService;

    @Autowired
    @Lazy
    private DirectoryScanService directoryScanService;

    @Autowired
    private Executor taskExecutor;

    // 用于文件监控的WatchService
    private volatile WatchService watchService;
    private volatile boolean monitoring = false;

    @Override
    public boolean startWork(String workspace) throws IOException {
        logger.info("=== 启动媒体资源管理服务工作流程 ===");
        logger.info("工作空间路径: {}", workspace);

        // 验证工作空间参数
        if (workspace == null || workspace.trim().isEmpty()) {
            throw new IllegalArgumentException("工作空间路径不能为空");
        }

        Path workspacePath = Paths.get(workspace);
        if (!Files.exists(workspacePath) || !Files.isDirectory(workspacePath)) {
            throw new IllegalArgumentException("指定的工作空间路径不存在或不是一个目录: " + workspace);
        }

        try {
            // 第一阶段：数据库初始化（主线程同步执行）
            logger.info("开始数据库初始化阶段...");
            initializeDatabase(workspace);
            logger.info("数据库初始化阶段完成");

            // 第二阶段：初始目录扫描（异步子线程执行）
            logger.info("开始初始目录扫描阶段...");
            CompletableFuture<Void> scanFuture = scanDirectoryAsync(workspace);
            logger.info("初始目录扫描已启动，将在后台执行");

            // 第三阶段：并行文件处理（线程池管理）
            logger.info("文件处理将使用线程池进行异步处理");

            // 第四阶段：服务状态管理
            logger.info("启动文件变更监控...");
            startFileMonitoring(workspace);

            logger.info("媒体资源管理服务启动成功");
            return true;

        } catch (Exception e) {
            logger.error("启动媒体资源管理服务失败: {}", e.getMessage(), e);
            throw new RuntimeException("启动媒体资源管理服务失败", e);
        }
    }

    /**
     * 初始化数据库
     */
    private void initializeDatabase(String workspace) throws SQLException {
        logger.info("初始化数据库，工作空间路径: {}", workspace);
        
        // 获取或创建数据源，这会自动初始化数据库表结构
        dataSourceManagerService.getOrCreateDataSource(workspace);
        
        logger.info("数据库初始化完成");
    }

    /**
     * 异步扫描目录
     */
    @Async("taskExecutor")
    private CompletableFuture<Void> scanDirectoryAsync(String workspace) {
        logger.info("异步开始目录扫描，工作空间: {}", workspace);

        try {
            // 使用NIO进行递归目录扫描，支持符号链接
            List<Content> contents = scanDirectoryRecursively(workspace);
            
            // 使用JDBC批量处理保存内容
            if (!contents.isEmpty()) {
                batchSaveContents(contents);
            }
            
            logger.info("目录扫描完成，共发现 {} 个文件", contents.size());
        } catch (Exception e) {
            logger.error("异步目录扫描失败: {}", e.getMessage(), e);
        }

        return CompletableFuture.completedFuture(null);
    }

    /**
     * 递归扫描目录，支持符号链接
     */
    private List<Content> scanDirectoryRecursively(String workspace) throws IOException {
        logger.info("开始递归扫描目录: {}", workspace);
        
        Path rootPath = Paths.get(workspace);
        List<Content> contents = new ArrayList<>();
        
        // 使用NIO Files.walkFileTree方法，支持符号链接
        Files.walkFileTree(rootPath, new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException {
                try {
                    // 检查是否为符号链接
                    if (Files.isSymbolicLink(path)) {
                        logger.debug("处理符号链接: {}", path);
                        // 解析符号链接指向的文件
                        Path realPath = path.toRealPath();
                        if (realPath != null && Files.isRegularFile(realPath)) {
                            // 检查是否为媒体文件
                            String fileName = realPath.getFileName().toString();
                            if (FileUtil.isValidMediaFile(realPath.toString())) {
                                Content content = createContentFromPath(realPath, fileName);
                                if (content != null) {
                                    contents.add(content);
                                    logger.debug("添加内容到列表: {}", realPath);
                                }
                            }
                        }
                    } else if (Files.isRegularFile(path)) {
                        // 检查是否为媒体文件
                        String fileName = path.getFileName().toString();
                        if (FileUtil.isValidMediaFile(path.toString())) {
                            Content content = createContentFromPath(path, fileName);
                            if (content != null) {
                                contents.add(content);
                                logger.debug("添加内容到列表: {}", path);
                            }
                        }
                    }
                } catch (Exception e) {
                    logger.warn("处理文件时出错: {}", path, e);
                }
                
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
                // 跳过隐藏目录和元数据目录
                String dirName = dir.getFileName().toString();
                if (dirName.startsWith(".") || dirName.equals("myMediaMeta")) {
                    logger.debug("跳过目录: {}", dir);
                    return FileVisitResult.SKIP_SUBTREE;
                }
                return FileVisitResult.CONTINUE;
            }
        });
        
        logger.info("递归扫描完成，共找到 {} 个媒体文件", contents.size());
        return contents;
    }

    /**
     * 从文件路径解析媒体文件信息
     * 该方法只解析媒体文件信息，不包括创建数据库实体对象
     *
     * @param filePath 文件路径
     * @return 包含媒体文件信息的对象
     */
    public MediaFileInfo parseMediaFileInfo(String filePath) {
        try {
            // 检查输入参数
            if (filePath == null || filePath.trim().isEmpty()) {
                logger.warn("文件路径为空，无法解析媒体信息");
                return null;
            }

            Path path = Paths.get(filePath);
            String fileName = path.getFileName().toString();
            File file = path.toFile();

            // 检查文件是否存在
            if (!file.exists()) {
                logger.warn("文件不存在，路径: {}", filePath);
                return null;
            }

            MediaFileInfo fileInfo = new MediaFileInfo();
            fileInfo.setFileName(fileName);
            fileInfo.setFilePath(filePath);
            fileInfo.setContentType(FileUtil.getContentType(fileName));
            fileInfo.setFileSizeBytes(file.length());
            fileInfo.setTitle(FileUtil.getFilenameWithoutExtension(fileName));

            // 根据内容类型提取额外元数据
            if ("video".equals(fileInfo.getContentType())) {
                // 提取视频元数据
                double duration = MediaUtil.getVideoDuration(filePath);
                fileInfo.setDurationSeconds(duration);

                int[] dimensions = MediaUtil.getVideoDimensions(filePath);
                if (dimensions != null) {
                    fileInfo.setWidth(dimensions[0]);
                    fileInfo.setHeight(dimensions[1]);
                }
            } else if ("image".equals(fileInfo.getContentType())) {
                // 提取图片元数据
                int[] dimensions = MediaUtil.getImageDimensions(filePath);
                if (dimensions != null) {
                    fileInfo.setWidth(dimensions[0]);
                    fileInfo.setHeight(dimensions[1]);
                }
            } else if ("audio".equals(fileInfo.getContentType())) {
                // 音频文件目前只提取基本元数据
                // 可以扩展以提取音频相关信息
            }

            return fileInfo;
        } catch (Exception e) {
            logger.error("解析媒体文件信息失败，路径: {}", filePath, e);
            return null;
        }
    }

    /**
     * 从文件路径创建Content对象
     */
    private Content createContentFromPath(Path path, String fileName) {
        try {
            String pathStr = path.toString();
            MediaFileInfo fileInfo = parseMediaFileInfo(pathStr);

            if (fileInfo == null) {
                return null;
            }

            Content content = new Content();
            content.setFilename(fileInfo.getFileName());
            content.setFilePath(fileInfo.getFilePath());
            content.setContentType(fileInfo.getContentType());
            content.setFileSizeBytes(fileInfo.getFileSizeBytes());
            content.setCreatedAt(LocalDateTime.now());
            content.setUpdatedAt(LocalDateTime.now());
            content.setTitle(fileInfo.getTitle());
            content.setDurationSeconds(fileInfo.getDurationSeconds());
            content.setWidth(fileInfo.getWidth());
            content.setHeight(fileInfo.getHeight());

            return content;
        } catch (Exception e) {
            logger.error("创建Content对象失败，路径: {}", path, e);
            return null;
        }
    }

    /**
     * 使用MyBatis Plus批量处理保存内容
     */
    private void batchSaveContents(List<Content> contents) {
        logger.info("开始批量保存 {} 个内容到数据库", contents.size());

        if (contents.isEmpty()) {
            logger.info("没有内容需要保存");
            return;
        }

        try {
            // 使用MyBatis Plus的批量操作方法
            // 首先检查哪些内容已存在，哪些不存在
            List<Content> contentsToInsert = new ArrayList<>();
            List<Content> contentsToUpdate = new ArrayList<>();
            
            for (Content content : contents) {
                // 检查是否已存在相同文件路径的内容
                com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<Content> wrapper = 
                    new com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<>();
                wrapper.eq("file_path", content.getFilePath());
                
                Content existingContent = contentMapper.selectOne(wrapper);
                
                if (existingContent != null) {
                    // 如果存在，则更新（保持ID不变）
                    content.setId(existingContent.getId());
                    contentsToUpdate.add(content);
                } else {
                    // 如果不存在，则插入
                    contentsToInsert.add(content);
                }
            }
            
            // 分批批量插入新内容
            if (!contentsToInsert.isEmpty()) {
                for (int i = 0; i < contentsToInsert.size(); i += 100) {
                    int endIndex = Math.min(i + 100, contentsToInsert.size());
                    List<Content> batch = contentsToInsert.subList(i, endIndex);
                    this.saveBatch(batch);  // 使用MyBatis Plus提供的saveBatch方法
                    logger.debug("已批量插入: {} - {}", i, endIndex);
                }
            }
            
            // 分批批量更新已存在的内容
            if (!contentsToUpdate.isEmpty()) {
                for (int i = 0; i < contentsToUpdate.size(); i += 100) {
                    int endIndex = Math.min(i + 100, contentsToUpdate.size());
                    List<Content> batch = contentsToUpdate.subList(i, endIndex);
                    this.updateBatchById(batch);  // 使用MyBatis Plus提供的updateBatchById方法
                    logger.debug("已批量更新: {} - {}", i, endIndex);
                }
            }

            logger.info("批量保存完成，共插入 {} 条记录，更新 {} 条记录", 
                       contentsToInsert.size(), contentsToUpdate.size());
        } catch (Exception e) {
            logger.error("批量保存内容失败", e);
            throw new RuntimeException("批量保存内容失败", e);
        }
    }

    /**
     * 从内容列表中获取工作空间路径
     */
    private String getWorkspaceFromContents(List<Content> contents) {
        if (contents != null && !contents.isEmpty()) {
            String firstPath = contents.get(0).getFilePath();
            if (firstPath != null) {
                // 提取第一个文件的父目录作为工作空间
                Path path = Paths.get(firstPath);
                return path.getParent().toString();
            }
        }
        return System.getProperty("user.home"); // 默认工作空间
    }

    /**
     * 启动文件变更监控
     */
    @Async("taskExecutor")
    private void startFileMonitoring(String workspace) {
        logger.info("开始启动文件监控，监控路径: {}", workspace);
        
        try {
            watchService = FileSystems.getDefault().newWatchService();
            Path path = Paths.get(workspace);
            
            // 注册监控事件
            path.register(watchService,
                StandardWatchEventKinds.ENTRY_CREATE,
                StandardWatchEventKinds.ENTRY_DELETE,
                StandardWatchEventKinds.ENTRY_MODIFY);
                
            monitoring = true;
            
            // 启动监控线程
            Thread monitorThread = new Thread(this::monitorChanges, "FileMonitorThread");
            monitorThread.setDaemon(true);
            monitorThread.start();
            
            logger.info("文件监控已启动，监控路径: {}", workspace);
        } catch (IOException e) {
            logger.error("启动文件监控失败: {}", e.getMessage(), e);
            monitoring = false;
        }
    }

    /**
     * 监控文件变更
     */
    private void monitorChanges() {
        logger.info("开始监控文件变更...");
        
        while (monitoring) {
            try {
                WatchKey key = watchService.take(); // 阻塞等待事件
                
                for (WatchEvent<?> event : key.pollEvents()) {
                    WatchEvent.Kind<?> kind = event.kind();
                    
                    // 如果事件是取消注册（OVERFLOW），则跳过
                    if (kind == StandardWatchEventKinds.OVERFLOW) {
                        continue;
                    }
                    
                    // 获取事件上下文（文件名）
                    WatchEvent<Path> ev = (WatchEvent<Path>) event;
                    Path filename = ev.context();
                    
                    logger.info("检测到文件变更 - 类型: {}, 文件: {}", kind.name(), filename);
                    
                    // 处理文件变更
                    handleFileEvent(kind, filename);
                }
                
                // 重置键，继续监控
                boolean valid = key.reset();
                if (!valid) {
                    logger.warn("WatchKey 无效，停止监控");
                    break;
                }
            } catch (InterruptedException e) {
                logger.info("文件监控线程被中断");
                Thread.currentThread().interrupt();
                break;
            } catch (Exception e) {
                logger.error("监控文件变更时发生错误", e);
            }
        }
        
        logger.info("文件监控已停止");
    }

    /**
     * 处理文件事件
     */
    private void handleFileEvent(WatchEvent.Kind<?> kind, Path filename) {
        // 这里可以根据事件类型处理文件
        // 创建/修改事件：重新扫描并更新数据库
        // 删除事件：从数据库中删除对应记录
        logger.debug("处理文件事件 - 类型: {}, 文件: {}", kind.name(), filename);
    }
}